﻿//  Copyright (c) 2009 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using DNA.Mvc.jQuery;
using System.Web.Helpers;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.ComponentModel;

namespace DNA.Mvc.Controllers
{
    public class WebFilesController : Controller
    {
        private IWebResourceService service;
        private WebSiteContext _context;
        public WebFilesController(WebSiteContext context, IWebResourceService svc) { service = svc; _context = context; }

        [SecurityAction("Create paths",
            PermssionSet = "Web file system",
            Description = "Allows user to create the directory on the server.",
            ThrowOnDeny = true,
            TitleResName = "SA_CreatePaths",
            DescResName = "SA_CreatePathsDesc",
            PermssionSetResName = "SA_WebResSystem")]
        [AcceptVerbs("MKCOL")]
        public ActionResult CreatePath()
        {
            service.CreatePath(HttpContext.Request.Url);
            return Json(new WebResourceInfo(HttpContext.Request.Url), JsonRequestBehavior.AllowGet);
        }

        public ActionResult CreatePath(Uri destination)
        {
            var isAuthorized = false;

            if (_context.Web.IsRoot)
            {
                isAuthorized = _context.IsAuthorized(this, "CreatePath");
            }
            else
            {
                if (User.IsWebOwner())
                    isAuthorized = true;
            }

            if (isAuthorized)
            {
                if (destination == null)
                    throw new ArgumentNullException("destination");
                service.CreatePath(destination);
                return Json(new WebResourceInfo(destination), JsonRequestBehavior.AllowGet);
            }
            return Json(new { }, JsonRequestBehavior.AllowGet);
        }

        [SecurityAction("Move files and paths",
            PermssionSet = "Web file system",
            Description = "Allows user can move the files and paths.",
            ThrowOnDeny = true,
            TitleResName = "SA_MoveFilesAndPaths",
            DescResName = "SA_MoveFilesAndPathsDesc",
            PermssionSetResName = "SA_WebResSystem")]
        [AcceptVerbs("MOVE")]
        public ActionResult Move(Uri destination)
        {
            service.Move(HttpContext.Request.Url, destination);
            return Json(new WebResourceInfo(destination), JsonRequestBehavior.AllowGet);
        }

        public ActionResult Move(Uri sourceUri, Uri destination)
        {
            var isAuthorized = false;

            if (_context.Web.IsRoot)
            {
                isAuthorized = _context.IsAuthorized(this, "Move");
            }
            else
            {
                if (User.IsWebOwner())
                    isAuthorized = true;
            }

            if (isAuthorized)
            {
                service.Move(sourceUri, destination);
                return Json(new WebResourceInfo(destination), JsonRequestBehavior.AllowGet);
            }
            else
                return Json(new { }, JsonRequestBehavior.AllowGet);
        }

        /// <summary>
        /// Gets path and files by specified path.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        [AcceptVerbs("LIST")]
        public ActionResult List(string path)
        {
            return Content("Not support");
        }

        [SecurityAction("List paths",
             PermssionSet = "Web file system",
             Description = "Allows user list the paths from web file system",
             ThrowOnDeny = true,
            TitleResName = "SA_ListPaths",
            DescResName = "SA_ListPathsDesc",
            PermssionSetResName = "SA_WebResSystem")]
        [AcceptVerbs("PLIST")]
        public ActionResult PathList()
        {
            var dirNodes = GetPathNodes(HttpContext.Request.Url);
            return Json(dirNodes, JsonRequestBehavior.AllowGet);
        }

        public ActionResult GetPaths(Uri url)
        {
            var isAuthorized = false;

            if (_context.Web.IsRoot)
            {
                isAuthorized = _context.IsAuthorized(this, "PathList");
            }
            else
            {
                if (User.IsWebOwner())
                    isAuthorized = true;
            }

            if (isAuthorized)
            {
                var dirNodes = GetPathNodes(url);
                return Json(dirNodes, JsonRequestBehavior.AllowGet);
            }
            else
                return Json(new { }, JsonRequestBehavior.AllowGet);
        }

        private List<NavigatableNode> GetPathNodes(Uri url)
        {
            //var url = HttpContext.Request.Url;
            var dirs = service.GetPaths(url);
            var dirNodes = new List<NavigatableNode>();
            foreach (var dir in dirs)
            {
                var info = new WebResourceInfo(dir);
                dirNodes.Add(new NavigatableNode()
                {
                    Text = info.Name,
                    Value = info.Url,
                    ImageUrl = Url.Content("~/content/images/folder.gif"),
                    NavigateUrl = "javascript:void(0);"
                });
            }
            return dirNodes;
        }

        [SecurityAction("List files",
            PermssionSet = "Web file system",
            Description = "Allows user list the files from path.",
            ThrowOnDeny = true,
            TitleResName = "SA_ListFiles",
            DescResName = "SA_ListFilesDesc",
            PermssionSetResName = "SA_WebResSystem")]
        [AcceptVerbs("FLIST")]
        [Pagable]
        public ActionResult FileList(QueryParams _params)
        {
            var uris = service.GetFiles(HttpContext.Request.Url);
            var skipCount = (_params.Index > 0 ? _params.Index - 1 : _params.Index) * _params.Size;
            var infos = (from url in uris
                         select new WebResourceInfo(url) { FileSize = GetFileSize(url) })
                         .Skip(skipCount)
                         .Take(_params.Size)
                         .ToList();

            return View(new ModelWrapper()
            {
                Model = infos,
                Total = uris.Count()
            });
        }

        private long GetFileSize(Uri url)
        {
            if (Url != null)
            {
                var path = service.MapPath(url);
                if (System.IO.File.Exists(path))
                {
                    var f = new System.IO.FileInfo(path);
                    return f.Length;
                }
            }
            return 0;
        }

        [Pagable]
        public ActionResult GetFiles(Uri url, QueryParams _params)
        {
            var isAuthorized = false;

            if (_context.Web.IsRoot)
            {
                isAuthorized = _context.IsAuthorized(this, "FileList");
            }
            else
            {
                if (User.IsWebOwner())
                    isAuthorized = true;
            }

            if (isAuthorized)
            {
                var uris = service.GetFiles(url);
                var total = 0;
                var infos = new List<WebResourceInfo>();

                if (uris != null)
                {
                    var skipCount = (_params.Index > 0 ? _params.Index - 1 : _params.Index) * _params.Size;
                    infos = (from _url in uris select new WebResourceInfo(_url) { FileSize = GetFileSize(_url) })
                                     .Skip(skipCount)
                                     .Take(_params.Size)
                                     .ToList();

                    total = uris.Count();
                }

                return View(new ModelWrapper()
                {
                    Model = infos,
                    Total = total
                });
            }
            else
            {
                return RedirectToAction("AccessDenied", "Security", new { Area = "" });
            }
        }

        [SecurityAction("Delete paths",
             PermssionSet = "Web file system",
             Description = "Allows user to delete the directory on the server.",
             ThrowOnDeny = true,
            TitleResName = "SA_DeletePaths",
            DescResName = "SA_DeletePathsDesc",
            PermssionSetResName = "SA_WebResSystem")]
        [HttpDelete]
        public ActionResult Delete()
        {
            try
            {
                service.Delete(HttpContext.Request.Url);
            }
            catch (Exception e)
            {
                return Content(e.Message);
            }
            return Content("OK");
        }

        public ActionResult Delete(Uri url)
        {
            var isAuthorized = false;

            if (_context.Web.IsRoot)
            {
                isAuthorized = _context.IsAuthorized(this, "Delete");
            }
            else
            {
                if (User.IsWebOwner())
                    isAuthorized = true;
            }

            if (isAuthorized)
            {
                try
                {
                    service.Delete(HttpContext.Request.Url);
                }
                catch (Exception e)
                {
                    return Content(e.Message);
                }
                return Content("OK");
            }

            throw new NotImplementedException();
            //return Content(Resources.language.AccessDenied_MSG);
        }

        private bool IsRequestFromExternalDomain(HttpRequestBase request)
        {
            if (request.UrlReferrer != null)
            {
                if ((request.Url.IsAbsoluteUri) && (request.UrlReferrer.IsAbsoluteUri))
                {
                    return !request.Url.Authority.Equals(request.UrlReferrer.Authority);
                }
            }
            return false;
        }

        [HttpGet, FileCache(Duration = 3600)]
        public ActionResult GetFile(string path, bool? thumb = false)
        {
            if (IsRequestFromExternalDomain(HttpContext.Request))
            {
                if (!_context.Web.IsTrusted(HttpContext.Request.Url, HttpContext.Request.UrlReferrer))
                {
                    if (!string.IsNullOrEmpty(_context.Web.MasterTools.UrlForUntrustLink))
                    {
                        var ulink = Url.Content(_context.Web.MasterTools.UrlForUntrustLink);
                        return File(ulink, FileUtility.GetContentType(ulink));
                    }
                    return HttpNotFound();
                    //throw new FileNotFoundException();
                }
            }
            var serverPath = service.MapPath(HttpContext.Request.Url);

            if (!System.IO.File.Exists(serverPath))
                return HttpNotFound();

            if (thumb.Value)
            {
                var info = new WebResourceInfo(HttpContext.Request.Url);
                return Thumb(serverPath, info);
            }
            return File(serverPath, FileUtility.GetContentType(serverPath));
        }

        public FileResult Thumb(string path, WebResourceInfo info)
        {
            //string fPath = System.IO.Path.GetDirectoryName(path);
            int width = 64;
            int height = 64;
            var wResult = ValueProvider.GetValue("w");
            var hResult = ValueProvider.GetValue("h");

            if (wResult != null)
                int.TryParse(wResult.AttemptedValue, out width);

            if (hResult != null)
                int.TryParse(hResult.AttemptedValue, out height);

            //var thumb = new WebImage(path);
            var stream = new MemoryStream();
            var image = Image.FromFile(path);
            var thumb= ResizeImage(image, height, width, preventEnlarge: true);
            thumb.Save(stream, GetImageFormat(info.ContentType));
            //var buffer = thumb.Resize(width, height, preventEnlarge: true).GetBytes();
            //stream.Write(buffer, 0, buffer.Length);
            stream.Position = 0;
            return File(stream, info.ContentType);

        }

        #region Image Resize helpers

        private static bool TryFromStringToImageFormat(string value, out ImageFormat result)
        {
            result = null;
            if (string.IsNullOrEmpty(value))
            {
                return false;
            }
            if (value.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
            {
                value = value.Substring("image/".Length);
            }
            value = NormalizeImageFormat(value);
            TypeConverter converter = TypeDescriptor.GetConverter(typeof(ImageFormat));
            try
            {
                result = (ImageFormat)converter.ConvertFromInvariantString(value);
            }
            catch (NotSupportedException)
            {
                return false;
            }
            return true;
        }

        private static string NormalizeImageFormat(string value)
        {
            value = value.ToLowerInvariant();
            switch (value)
            {
                case "jpeg":
                case "jpg":
                case "pjpeg":
                    return "jpeg";

                case "png":
                case "x-png":
                    return "png";

                case "icon":
                case "ico":
                    return "icon";
            }
            return value;
        }

        private static ImageFormat GetImageFormat(string format)
        {
            ImageFormat format2;
            if (!TryFromStringToImageFormat(format, out format2))
            {
                throw new ArgumentException(string.Format("Incorrect Image Format {0}", format ), "format");
            }
            return format2;
        }

        private static Image ResizeImage(Image image, int height, int width, bool preserveAspectRatio = true, bool preventEnlarge = false)
        {
            if (preserveAspectRatio)
            {
                double num3 = (height * 100.0) / ((double)image.Height);
                double num4 = (width * 100.0) / ((double)image.Width);
                if (num3 > num4)
                {
                    height = (int)Math.Round((double)((num4 * image.Height) / 100.0));
                }
                else if (num3 < num4)
                {
                    width = (int)Math.Round((double)((num3 * image.Width) / 100.0));
                }
            }
            
            if (preventEnlarge)
            {
                if (height > image.Height)
                {
                    height = image.Height;
                }
                if (width > image.Width)
                {
                    width = image.Width;
                }
            }
            if ((image.Height == height) && (image.Width == width))
            {
                return image;
            }
            return GetBitmapFromImage(image, width, height, true);
        }

        private static Bitmap GetBitmapFromImage(Image image, int width, int height, bool preserveResolution=true)
        {
            bool flag = (((image.PixelFormat == PixelFormat.Format1bppIndexed) || (image.PixelFormat == PixelFormat.Format4bppIndexed)) || (image.PixelFormat == PixelFormat.Format8bppIndexed)) || (image.PixelFormat == PixelFormat.Indexed);
            Bitmap bitmap = flag ? new Bitmap(width, height) : new Bitmap(width, height, image.PixelFormat);
            if (preserveResolution)
            {
                bitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);
            }
            else
            {
                bitmap.SetResolution(96f, 96f);
            }
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                if (flag)
                {
                    graphics.FillRectangle(Brushes.Transparent, 0, 0, width, height);
                }
                //graphics.InterpolationMode = InterpolationMode.High;
                graphics.DrawImage(image, 0, 0, width, height);
            }
            return bitmap;
        }

        #endregion

        [AcceptVerbs("COPY")]
        public ActionResult Copy(string path, string destination)
        {
            return Content("Not support");
        }

        //[HttpPut]
        //public ActionResult Replace(string path, HttpFileCollection files)
        //{
        //    return View();
        //}

        [SecurityAction("Upload files",
             PermssionSet = "Web file system",
             Description = "Allows user to upload the file to the server.",
             ThrowOnDeny = true,
            TitleResName = "SA_UploadFiles",
            DescResName = "SA_UploadFilesDesc",
            PermssionSetResName = "SA_WebResSystem")]
        [HttpPost]
        public ActionResult Upload(string path)
        {
            try
            {
                if (Request.Files.Count == 0)
                {
                    HttpContext.Response.StatusCode = 204;
                    return Content("No Content");
                }

                foreach (string key in Request.Files.Keys)
                {
                    var file = Request.Files[key];
                    var fileInfo = new FileInfo(file.FileName);

                    if (!_context.RootWeb.IsAllowUpload(fileInfo.Extension))
                    {
                        HttpContext.Response.StatusCode = 415;
                        return Content(string.Format(Resources.language.WebFilesController_Unsupported, "\"" + fileInfo.Extension + "\""));
                    }

                    if (file.ContentLength > (_context.RootWeb.MaximumFileSize * 1000000))
                    {
                        HttpContext.Response.StatusCode = 403;
                        return Content(Resources.language.WebFilesController_Forbidden);
                    }

                    service.SaveFile(FileUtility.ReadStream(file.InputStream), file.FileName, HttpContext.Request.Url);
                    //Web.SaveFile(FileUtilty.ReadStream(file.InputStream),fileInfo.Name,HttpContext.Request.Url);
                }
            }
            catch (Exception e)
            {
                HttpContext.Response.StatusCode = 424;
                return Content("Method Failure - " + e.Message);
            }

            HttpContext.Response.StatusCode = 201;
            return Content("OK");
        }

        #region UI Actions

        [SiteControlPanel(ResKey = "RES_FILE_MGT", Sequence = 4)]
        [MyControlPanel(ResKey = "RES_FILE_MGT", Sequence = 4, ShowInPersonalSiteOnly = true)]
        [SecurityAction("File manager",
             PermssionSet = "Web file system",
             Description = "Allows user use file manager to manage web resources.",
            TitleResName = "SA_FileMan",
            DescResName = "SA_FileManDesc",
            PermssionSetResName = "SA_WebResSystem")]
        public ActionResult Explorer()
        {
            _InitExplorer(ViewData);
            return PartialView("FileExplorer");
        }

        private void _InitExplorer(ViewDataDictionary viewData)
        {
            var rootPath = _context.ApplicationPath + "/webshared/" + _context.Web.Name + "/";
            var rootUri = new Uri(rootPath);
            var phyicalPath = service.MapPath(rootUri);
            if (!Directory.Exists(phyicalPath))
                Directory.CreateDirectory(phyicalPath);
            viewData["RootPath"] = rootPath;
        }

        //[SiteControlPanel]
        public ActionResult Dialog()
        {
            _InitExplorer(ViewData);
            return PartialView("FileDialog");
        }

        public ActionResult MimeIcon(string extension)
        {
            var ext = extension.Replace(".", "");
            var fileName = Server.MapPath("~/content/images/mime/" + ext + ".gif");
            if (System.IO.File.Exists(fileName))
                return File(fileName, "image/gif");
            return File(Server.MapPath("~/content/images/mime/unknow.gif"), "image/gif");
        }

        [Authorize]
        public ActionResult ListView(string path)
        {
            ViewData["Path"] = path;
            return PartialView();
        }

        #endregion

        ////[FileCache]
        //public ActionResult CompositeScripts(string id)
        //{
        //    if (string.IsNullOrEmpty(id))
        //        throw new FileNotFoundException();
        //    string[] scriptReferences = id.Split(',');
        //    string[] _scripts = scriptReferences.Distinct().ToArray();
        //    var compositeScriptBuilder = new StringBuilder();

        //    foreach (var filePath in _scripts)
        //    {
        //        string fileName = Server.MapPath("~/scripts/" + filePath.Replace("`", "/"));
        //        if (System.IO.File.Exists(fileName))
        //            compositeScriptBuilder.Append(System.IO.File.ReadAllText(fileName, Encoding.UTF8));
        //    }
        //    var scriptByts = Encoding.UTF8.GetBytes(compositeScriptBuilder.ToString());
        //    return File(scriptByts, "application/x-javascript");
        //}
    }
}
